package lambdasinaction.chap5.my;

import static java.util.stream.Collectors.joining;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * TODO
 *
 * @author Fish
 * @since 0.0.1 2018/7/25 17:50
 */
public class MyPuttingIntoPractice {

  private static List<MyTransaction> transactions = null;

  static {
    MyTrader raoul = new MyTrader("Raoul", "Cambridge");
    MyTrader mario = new MyTrader("Mario", "Milan");
    MyTrader alan = new MyTrader("Alan", "Cambridge");
    MyTrader brian = new MyTrader("Brian", "Cambridge");

    transactions = Arrays.asList(
        new MyTransaction(brian, 2011, 300),
        new MyTransaction(raoul, 2012, 1000),
        new MyTransaction(raoul, 2011, 400),
        new MyTransaction(mario, 2012, 710),
        new MyTransaction(mario, 2012, 700),
        new MyTransaction(alan, 2012, 950)
    );
  }

  public static void main(String[] args) {
    // 问题1：找出2011年发生的所有交易，并按交易额排序（从低到高）
    List<MyTransaction> tr2011 =
        transactions.stream()
            .filter(t -> t.getYear() == 2011) // 给 filter 传递一个谓词来选择2011年的交易
            .sorted(Comparator.comparing(MyTransaction::getValue)) // 按照交易额进行排序
            .collect(Collectors.toList()); // 将生成的 Stream 中的所有元素收集到一个 list 中
    System.out.println("tr2011:" + tr2011);

    // 问题2：交易员都在哪些不同的城市工作过
    List<String> cities =
        transactions.stream()
            .map(t -> t.getTrader().getCity()) // 提取交易相关的每位交易员所在的城市
            .distinct() // 去重，只选择互不相同的城市
            .collect(Collectors.toList()); // 将生成的 Stream 中的所有元素收集到一个 list 中
    System.out.println(cities);
    // 另外一种方式，可以去掉 distinct()，改用 toSet()，这样就会把流转换为集合
    Set<String> citySet =
        transactions.stream()
            .map(t -> t.getTrader().getCity())
            .collect(Collectors.toSet());
    System.out.println("citySet:" + citySet);

    // 问题3：查找所有来自剑桥的交易员，并按姓名排序
    List<MyTrader> traders =
        transactions.stream()
            .map(MyTransaction::getTrader) // 从交易中提取所有交易员
            .filter(t -> t.getCity().equals("Cambridge")) // 仅选择位于剑桥的交易员
            .distinct() // 确保没有任何重复
            .sorted(Comparator.comparing(MyTrader::getName)) // 对生成的交易员流按姓名排序
            .collect(Collectors.toList()); // 将生成的 Stream 中的所有元素收集到一个 list 中
    System.out.println(traders);

    // 问题4：返回所有交易员的姓名字符串，按字母顺序排序
    List<String> names =
        transactions.stream()
            .map(t -> t.getTrader().getName()) // 提取所有交易员姓名，生成一个 Strings 构成的Stream
            .distinct() // 去重，只选择不同的姓名
            .sorted() // 对姓名按字母顺序排序
            .collect(Collectors.toList()); // 将生成的 Stream 中的所有元素收集到一个 list 中
    String traderStr =
        transactions.stream()
            .map(t -> t.getTrader().getName())
            .distinct()
            .sorted()
            .reduce("", (n1, n2) -> n1 + n2); // 逐个拼接每个名字，得到一个将所有名字连接起来的String
    // 请注意，此解决方案效率不高（所有字符串都被反复连接，每次迭代的时候都建立一个新的 String对象）
    // 下一章中，你将看到一个更为高效的解决方案，它像下面这样使用joining(其内部会用到StringBuilder)
    String traderStr2 =
        transactions.stream()
            .map(t -> t.getTrader().getName())
            .distinct()
            .sorted()
            .collect(joining(","));
    System.out.println("names: " + traderStr2);

    // 问题5：有没有交易员是在米兰工作的
    boolean isExistInMilan =
        transactions.stream()
            .anyMatch(
                t -> t.getTrader().getCity().equals("Milan")); // 把一个谓词传递给anyMatch,检查是否有交易员在米兰工作
    System.out.println("isExistInMilan: " + isExistInMilan);

    // 问题6：打印生活在剑桥的交易员的所有交易额
    transactions.stream()
        .filter(t -> t.getTrader().getCity().equals("Cambridge")) // 选择住在剑桥的交易员所进行的交易
        .map(MyTransaction::getValue) // 提取这些交易的交易额
        .forEach(System.out::println); // 打印每个值

    // 问题7：所有交易中，最高的交易额是多少
    int max = transactions.stream()
        .mapToInt(MyTransaction::getValue)
        .max().getAsInt();
    System.out.println(max);
    Optional<Integer> highestValue =
        transactions.stream()
            .map(MyTransaction::getValue) // 提取每项交易的交易额
            .reduce(Integer::max); // 计算生成的流中的最大值

    // 问题8：找到交易额中最小的交易
    int min = transactions.stream()
        .mapToInt(MyTransaction::getValue)
        .min().getAsInt();
    System.out.println(min);
    // 流支持min 和 max方法，它们可以接受一个Comparator作为参数，指定计算最小或最大值时要比较哪个键值
    Optional<MyTransaction> smallestTransaction =
        transactions.stream()
        .min(Comparator.comparing(MyTransaction::getValue));
  }

}
